package blazingbubble.graphics.game.renderer;

import blazingbubble.config.Config;
import blazingbubble.config.MultiplayerFieldInformation;
import blazingbubble.exception.BlazingRuntimeException;
import blazingbubble.game.Player;
import blazingbubble.graphics.game.StaticSpriteMap;
import blazingbubble.game.Bubble;
import blazingbubble.game.Field;
import blazingbubble.exception.StartupException;
import moltencore.game.CannonData;
import moltencore.game.loader.LevelSprite;
import org.xml.sax.SAXException;
import spark.graphics.scenegraph.*;
import spark.graphics.*;
import spark.exception.LoadingException;
import spark.math.Vector2;
import spark.math.Vector3;
import spark.math.Geometry;

import java.util.HashMap;
import java.io.File;
import java.util.Iterator;

import moltencore.game.bubble.Type;

public class FieldRenderer {
	private ISpriteMap spriteMap;
	private HashMap<String, ISpriteMap> spriteMaps = new HashMap<String, ISpriteMap>();
	private String spriteMapPath;
	private String themePath;

	private Font font;
	private HashMap<Type, Sprite> bubbleSprites = new HashMap<Type, Sprite>();
	//private SpriteRenderable background = new SpriteRenderable();
	private SpriteRenderable cannon = new SpriteRenderable();
	private Vector2 up = new Vector2(0, 1);

	public FieldRenderer(String spriteMapPath, String themePath) {
		this.spriteMapPath = spriteMapPath;
		this.themePath = themePath;
	}

	public void startup() throws StartupException {
		try {
			spriteMap = new SpriteMap(spriteMapPath, "crayon", themePath);
			bubbleSprites.put(Type.Red, spriteMap.getSprite("red"));
			bubbleSprites.put(Type.Green, spriteMap.getSprite("green"));
			bubbleSprites.put(Type.Blue, spriteMap.getSprite("blue"));
			bubbleSprites.put(Type.Orange, spriteMap.getSprite("orange"));
			bubbleSprites.put(Type.Violet, spriteMap.getSprite("violet"));
			bubbleSprites.put(Type.Yellow, spriteMap.getSprite("yellow"));
			bubbleSprites.put(Type.White, spriteMap.getSprite("white"));
			bubbleSprites.put(Type.Black, spriteMap.getSprite("black"));
			bubbleSprites.put(Type.Blazing, spriteMap.getSprite("blazing"));
			bubbleSprites.put(Type.Ghost, spriteMap.getSprite("stone"));
			bubbleSprites.put(Type.Rainbow, spriteMap.getSprite("rainbow"));
			cannon.setSprite(spriteMap.getSprite("cannon"));
			cannon.setDimensions(new Vector2(56f/32f, 163f/32f));
			font = Font.makeFont(Config.getSpriteMapsPath() + "/manaspace.sm", Config.getTexturesPath() + "/");
		}
		catch(LoadingException e) {
			throw new StartupException(e);
		}
	}

	public void prepareRender(Field field, ISpriteRenderable resultSprite, SceneGraph graph, MultiplayerFieldInformation.FieldInformation fieldInformation) {
		TransformationNode fieldTransformation = new TransformationNode(
				new Vector3(fieldInformation.getPosition()),
				null,
				new Vector3(fieldInformation.getScale(), fieldInformation.getScale(), 1.0f));
		graph.getRoot().addChild(fieldTransformation);

		try {
			field.getReadLock().lock();
			for(LevelSprite levelSprite : field.getLevelSprites()) {
				if(!levelSprite.getRenderable().isRenderReady()) {
					makeRenderReady(levelSprite);
				}
				TranslationNode levelSpriteTransformation = new TranslationNode(levelSprite.getRenderable().getPosition());
				levelSpriteTransformation.addChild(new SpriteNode().registerForRendering(levelSprite.getRenderable()));
				fieldTransformation.addChild(levelSpriteTransformation);
			}
		}
		finally {
			field.getReadLock().unlock();
		}

		try {
			field.getReadLock().lock();
			for(Bubble bubble : field.getBubbles()) {
				TranslationNode bubbleTranslation = null;
				bubble.getReadLock().lock();
				try {
					if(!bubble.isRenderReady()) {
						bubble.setSprite(bubbleSprites.get(bubble.getType()));
					}
					bubbleTranslation = new TranslationNode(bubble.getPosition());
					bubbleTranslation.addChild(new SpriteNode().registerForRendering(bubble));
				}
				finally {
					bubble.getReadLock().unlock();
				}
				fieldTransformation.addChild(bubbleTranslation);
			}
		}
		finally {
			field.getReadLock().unlock();
		}

		try {
			field.getReadLock().lock();
			for(CannonData cannonData : field.getCannonData()) {
				float rotation = Geometry.radianToDegree(up.angle(Vector2.normalize(cannonData.getTarget())));
				TransformationNodeZRotation cannonTransformation = new TransformationNodeZRotation(cannonData.getPosition(), rotation, null);
				fieldTransformation.addChild(cannonTransformation);
				cannonTransformation.addChild(new SpriteNode().registerForRendering(cannon));
			}
		}
		finally {
			field.getReadLock().unlock();
		}

		if(resultSprite != null) {
			TranslationNode resultSpriteTranslation = new TranslationNode(field.getResultPosition());
			resultSpriteTranslation.addChild(new SpriteNode().registerForRendering(resultSprite));
			fieldTransformation.addChild(resultSpriteTranslation);
		}
		TranslationNode nickTranslation = new TranslationNode(field.getNickPosition());
		String playerNicks;
		try {
			field.getReadLock().lock();
			Iterator<Player> playerIterator = field.getOwners().iterator();
			Player player = playerIterator.next();
			playerNicks = player.getName();
			while(playerIterator.hasNext()) {
				player = playerIterator.next();
				playerNicks += " & " + player.getName();
			}
		}
		finally {
			field.getReadLock().unlock();
		}
		nickTranslation.addChild(font.getText(playerNicks));
		fieldTransformation.addChild(nickTranslation);

		TranslationNode scoreTranslation = new TranslationNode(field.getScorePosition());
		scoreTranslation.addChild(font.getText(Integer.toString((int) field.getScore())));
		fieldTransformation.addChild(scoreTranslation);
	}

	private void makeRenderReady(LevelSprite levelSprite) {
		String key = makeKey(levelSprite);
		if(!spriteMaps.containsKey(key)) {
			try {
				spriteMaps.put(key, new SpriteMap(Config.getSpriteMapsPath() + "/" + levelSprite.getSpriteMap(), levelSprite.getTheme(), Config.getTexturesPath() + "/"));
			}
			catch(LoadingException e) {
				throw new BlazingRuntimeException(e);
			}
		}
		levelSprite.getRenderable().setSprite(spriteMaps.get(key).getSprite(levelSprite.getId()));
	}

	private String makeKey(LevelSprite sprite) {
		return sprite.getSpriteMap() + ":" + sprite.getTheme();
	}

	public void shutdown() {
		for(Sprite sprite : bubbleSprites.values()) {
			sprite.release();
		}
		//background.getSprite().release();
		cannon.getSprite().release();
	}
}
